/*
 * ATM Example system - file ATM.java
 *
 * copyright (c) 2001 - Russell C. Bjork
 *
 */
// HI MIKE
package atm;

import java.net.InetAddress;
import atm.physical.*;
import banking.Card;
import banking.Money;

/**
 * Representation for the ATM itself. An object of this class "owns" the objects
 * representing the component parts of the ATM, and the communications network,
 * and is responsible for creating customer sessions which then use it to gain
 * access to the component parts. This is an active class - when an instance of
 * the class is created, a thread is executed that actually runs the system.
 */

public class ATM implements Runnable {
	/**
	 * Constructor
	 * 
	 * @param id
	 *            the unique ID for this ATM
	 * @param place
	 *            the physical location of this ATM
	 * @param bankName
	 *            the name of the bank owning this ATM
	 * @param bankAddress
	 *            the Internet address of the bank
	 */
	public ATM(int id, String place, String bankName, InetAddress bankAddress) {
		this.id = id;
		this.place = place;
		this.bankName = bankName;
		this.bankAddress = bankAddress;

		// Create objects corresponding to component parts

		log = new Log();
		cardReader = new CardReader(this);
		cashDispenser = new CashDispenser(log);
		customerConsole = new CustomerConsole();
		envelopeAcceptor = new EnvelopeAcceptor(log);
		networkToBank = new NetworkToBank(log, bankAddress);
		operatorPanel = new OperatorPanel(this);
		receiptPrinter = new ReceiptPrinter();

		// Set up initial conditions when ATM first created

		state = OFF_STATE;
		switchOn = false;
		cardInserted = false;
	}

	// Methods corresponding to major responsibilities of the ATM

	/**
	 * The main program/applet will create a Thread that executes this code.
	 */
	public void run() {
		Session currentSession = null;

		while (true) {
			switch (state) {
			case OFF_STATE:

				customerConsole.display("Not currently available");

				synchronized (this) {
					try {
						wait();
					} catch (InterruptedException e) {
					}
				}

				if (switchOn) {
					performStartup();
					state = IDLE_STATE;
				}

				break;

			case IDLE_STATE:

				customerConsole.display("Please insert your card");
				cardInserted = false;

				synchronized (this) {
					try {
						wait();
					} catch (InterruptedException e) {
					}
				}

				if (cardInserted) {
					currentSession = new Session(this);
					state = SERVING_CUSTOMER_STATE;
				} else if (!switchOn) {
					performShutdown();
					state = OFF_STATE;
				}

				break;

			case SERVING_CUSTOMER_STATE:

				// The following will not return until the session has
				// completed

				currentSession.performSession();

				state = IDLE_STATE;

				break;

			}
		}
	}

	/**
	 * Inform the ATM that the switch on the operator console has been moved to
	 * the "on" position.
	 */
	public synchronized void switchOn() {
		switchOn = true;
		notify();
	}

	/**
	 * Inform the ATM that the switch on the operator console has been moved to
	 * the "off" position.
	 */
	public synchronized void switchOff() {
		switchOn = false;
		notify();
	}

	/**
	 * Inform the ATM that a card has been inserted into the card reader.
	 */
	public synchronized void cardInserted() {
		cardInserted = true;
		notify();
	}

	// The following methods allow objects of other classes to access component
	// parts of the ATM

	/**
	 * Accessor for id
	 * 
	 * @return unique id of this ATM
	 */
	public int getID() {
		return id;
	}

	/**
	 * Accessor for place
	 * 
	 * @return physical location of this ATM
	 */
	public String getPlace() {
		return place;
	}

	/**
	 * Accessor for bank name
	 * 
	 * @return name of bank owning this ATM
	 */
	public String getBankName() {
		return bankName;
	}

	/**
	 * Accessor for card reader
	 * 
	 * @return card reader component of this ATM
	 */
	public CardReader getCardReader() {
		return cardReader;
	}

	/**
	 * Accessor for cash dispenser
	 * 
	 * @return cash dispenser component of this ATM
	 */
	public CashDispenser getCashDispenser() {
		return cashDispenser;
	}

	/**
	 * Accessor for customer console
	 * 
	 * @return customer console component of this ATM
	 */
	public CustomerConsole getCustomerConsole() {
		return customerConsole;
	}

	/**
	 * Accessor for envelope acceptor
	 * 
	 * @return envelope acceptor component of this ATM
	 */
	public EnvelopeAcceptor getEnvelopeAcceptor() {
		return envelopeAcceptor;
	}

	/**
	 * Accessor for log
	 * 
	 * @return log component of this ATM
	 */
	public Log getLog() {
		return log;
	}

	/**
	 * Accessor for network to bank
	 * 
	 * @return network connection to bank of this ATM
	 */
	public NetworkToBank getNetworkToBank() {
		return networkToBank;
	}

	/**
	 * Accessor for operator panel
	 * 
	 * @return operator panel component of this ATM
	 */
	public OperatorPanel getOperatorPanel() {
		return operatorPanel;
	}

	/**
	 * Accessor for receipt printer
	 * 
	 * @return receipt printer component of this ATM
	 */
	public ReceiptPrinter getReceiptPrinter() {
		return receiptPrinter;
	}

	// Private methods

	/**
	 * Perform the System Startup use case when switch is turned on
	 */
	private void performStartup() {
		Money initialCash = operatorPanel.getInitialCash();
		cashDispenser.setInitialCash(initialCash);
		networkToBank.openConnection();
	}

	/**
	 * Perform the System Shutdown use case when switch is turned off
	 */
	private void performShutdown() {
		networkToBank.closeConnection();
	}

	// Instance variables recording information about the ATM

	/**
	 * Unique ID for this ATM
	 */
	private int id;

	/**
	 * Physical location of this ATM
	 */
	private String place;

	/**
	 * Name of the bank owning this ATM
	 */
	private String bankName;

	/**
	 * Internet address of the bank
	 */
	private InetAddress bankAddress;

	// Instance variables referring to the omponent parts of the ATM

	/**
	 * The ATM's card reader
	 */
	private CardReader cardReader;

	/**
	 * The ATM's cash dispenser
	 */
	private CashDispenser cashDispenser;

	/**
	 * The ATM's customer console
	 */
	private CustomerConsole customerConsole;

	/**
	 * The ATM's envelope acceptor
	 */
	private EnvelopeAcceptor envelopeAcceptor;

	/**
	 * The ATM's log
	 */
	private Log log;

	/**
	 * The ATM's network connection to the bank
	 */
	private NetworkToBank networkToBank;

	/**
	 * The ATM's operator panel
	 */
	private OperatorPanel operatorPanel;

	/**
	 * The ATM's receipt printer
	 */
	private ReceiptPrinter receiptPrinter;

	// State information

	/**
	 * The current state of the ATM - one of the possible values listed below
	 */
	private int state;

	/**
	 * Becomes true when the operator panel informs the ATM that the switch has
	 * been turned on - becomes false when the operator panel informs the ATM
	 * that the switch has been turned off.
	 */
	private boolean switchOn;

	/**
	 * Becomes true when the card reader informs the ATM that a card has been
	 * inserted - the ATM will make this false when it has tried to read the
	 * card
	 */
	private boolean cardInserted;

	// Possible values for state

	/**
	 * The ATM is off. The switch must be turned on before it can operate
	 */
	private static final int OFF_STATE = 0;

	/**
	 * The ATM is on, but idle. It can service a customer, or it can be shut
	 * down
	 */
	private static final int IDLE_STATE = 1;

	/**
	 * The ATM is servicing a customer.
	 */
	private static final int SERVING_CUSTOMER_STATE = 2;
}
